<?php 

/**
 * @property LsActiveRecord $owner
 */
class LsActiveRelatedDataProviderBehavior
extends CActiveRecordBehavior
{
	
	public $dataProviderClass = 'LsActiveDataProvider';
	
	
	public function getRelatedDataProvider($relationName,$config=array(),$queryCriteria=array()){
		$ar = $this->getOwner();
		$md = $ar->getMetaData();
		if(!isset($md->relations[$relationName])){
			throw new CDbException(Yii::t('yii','{class} does not have relation "{name}".',
				array('{class}'=>get_class($ar), '{name}'=>$relationName)));
		}
		
		$relation = $md->relations[$relationName];
		if(false/*($relation instanceof CHasManyRelation && $relation->through)*/
		|| ($relation instanceof CBelongsToRelation && !$relation->through)){
			throw new CDbException(Yii::t('p_yii','Ошибка связи {class}.{name}',
				array('{class}'=>get_class($ar), '{name}'=>$relationName)));
		}
		
		// return new CArrayDataProvider($ar->getRelated($relationName,true,$params), $config);
		
		// копируем критерии из связи
		$criteria = clone CActiveRecord::model($relation->className)->getDbCriteria();
		$criteria->distinct = true;
		foreach(array('select', 'condition', 'limit', 'offset', 'order', 'group', 'join', 'having', 'with', 'alias', 'index', 'together') as $param){
			if(!$relation->hasProperty($param) && !property_exists($relation, $param)){
				continue;
			}
			$criteria->$param = $relation->$param;
		}
		
		// склеиваем параметры с критерией
		$criteria->mergeWith(
			is_string($queryCriteria)
				? array('condition' => $queryCriteria)
				: $queryCriteria
		);
		
		// добавляем связь через fk
		$this->buidRelationCriteria($criteria, $relation);
		
		// собираем кофиг для LsActiveDataProvider
		$dpConfig = array(
			'criteria' => $criteria,
		) + $config;
		
		//echo "LsActiveDataProvider( {$relation->className} , criteria: ".var_export($dpConfig['criteria']->toArray(),true)." )";
		
		// долгожданный LsActiveDataProvider !!!!!11
		$dataProvider = new $this->dataProviderClass(
			$relation->className,
			$dpConfig
		);
		
		if(!($dataProvider instanceof LsActiveDataProvider)){
			throw new CDbException(Yii::t('p_yii','{class} не является потомком LsActiveDataProvider.',
				array('{class}'=>get_class($dataProvider))));
		}
		
		return $dataProvider;
	}
	
	
	protected function getChildRelation(CActiveRelation $relation){
		if((property_exists($relation, 'through') || $relation->canGetProperty('through'))
		&& $relation->through){
			return $this->getOwner()->getMetaData()->relations[$relation->through];
		}
	}
	
	
	protected function replaceAliasesInCriteria(CDbCriteria $criteria, $alias, $childAlias=null){
		$replace = array(
			'{%this}' => $alias,
			'{%child}' => $childAlias,
		);
		$replace_in = array('condition', 'join', 'order', 'group', 'having');
		foreach($replace_in as $key){
			$criteria->$key = strtr($criteria->$key, $replace);
		}
	}
	
	
	protected function buidRelationCriteria(CDbCriteria $criteria, CActiveRelation $relation, $parentModel=null, $parentRelation=null){
		$ar = $this->getOwner();
		$schema = $ar->getDbConnection()->getSchema();
		$useJoin = (bool) $parentModel;
		
		$parentModel = $parentModel ? $parentModel : $ar;
		$parentAlias = $parentModel->getTableAlias(true);
		$parentTable = $schema->quoteTableName($parentModel->tableName()).' '.$parentAlias;
		$parentMd = $parentModel->getMetaData();
		$parentSchema = $parentModel->getTableSchema();
		
		$childModel = CActiveRecord::model($relation->className);
		$childAlias = $childModel->getTableAlias(true);
		$childTable = $schema->quoteTableName($childModel->tableName()).' '.$childAlias;
		$childCriteria = $childModel->getDbCriteria();
		$childSchema = $childModel->getTableSchema();
		
		$criteria->params += $relation->params;
		
		$childRelation = $this->getChildRelation($relation);
		
		if($useJoin){
		//	var_dump($relation);
			
			$joinConditions = array();
			if($parentRelation instanceof CHasManyRelation){
				$parentKey = $parentRelation->foreignKey;
				$childKey = $childSchema->primaryKey;
			//	var_dump('$childSchema->foreignKeys = ', $childSchema->foreignKeys);
			//	var_dump('$parentSchema->foreignKeys = ', $parentSchema->foreignKeys);
				if(isset($parentSchema->foreignKeys[$relation->foreignKey])){
				//	var_dump("FK EXISTS: ", $parentSchema->foreignKeys[$relation->foreignKey]);
					$parentKey = $relation->foreignKey;
					$childKey = $parentSchema->foreignKeys[$relation->foreignKey][1];
				}
				$joinConditions[] =
					"({$parentAlias}.".$schema->quoteColumnName($parentKey)
					."={$childAlias}.".$schema->quoteColumnName($childKey).")"
				;
			}else{
				$joinConditions[] =
					"({$parentAlias}.".$schema->quoteColumnName($parentSchema->primaryKey)
					."={$childAlias}.".$schema->quoteColumnName($parentRelation->foreignKey).")"
				;
			}
			if($relation->condition !== ''){
				$criteria->addCondition($relation->condition);
			}
			if($childCriteria->condition !== ''){
				$criteria->addCondition($childCriteria->condition);
				$criteria->params += $childCriteria->params;
			}
			
		//	var_dump($joinConditions);
			
			$criteria->join .= " {$relation->joinType} $childTable";
			$criteria->join .= ' ON '.join(' AND ', $joinConditions);
		}
		
		$this->replaceAliasesInCriteria(
			$criteria,
			$parentAlias,
			$childAlias
		);
		
		
		
		if($childRelation){
			// продолжаем цепочку join'ов
			$this->buidRelationCriteria($criteria, $childRelation, $childModel, $relation);
		}else{
			// что именно выбираем
			$paramName = ':ardpfk';
			$criteria->params[$paramName] = $ar->getPrimaryKey();
			$criteria->addCondition(
				"{$childAlias}.".$schema->quoteColumnName($relation->foreignKey)
				."={$paramName}"
			);
		}
		
	}
	
}


?>